package texas_holdem.data;


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Set;

import texas_holdem.data.Card.CardNumber;
import texas_holdem.data.Card.CardType;

import texas_holdem.game.Hand;



public class OddsCalculator {
	
	protected static HashMap<CardNumber, Double> _numValues;
	// hand values
	protected static HashMap<Double, Integer> _handValues;
	protected static HashMap<Double, Integer> _flushHandValues;
	// odds before flop
	protected HashMap<Hand, Double> _oddsBeforeFlop;
	
	
	public OddsCalculator(String fileName)
	{
		initNumValues();
		try {
			InputStream handEval = new FileInputStream("docs\\handEval.txt"); 
			initHandValues(handEval);
			InputStream fstream = new FileInputStream(fileName);
			readOddsFromFile(fstream);
			
			handEval.close();
			fstream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
	}
	public OddsCalculator(InputStream initOdds, InputStream handEval)
	{
		initNumValues();
		initHandValues(handEval);
		readOddsFromFile(initOdds);
	}

	private void readOddsFromFile(InputStream fstream) {
		_oddsBeforeFlop = new HashMap<Hand,Double>();
		if (fstream == null) return;
		try{
			  // Open the file that is the first 
			  // command line parameter
			  
			  // Get the object of DataInputStream
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));
			  String strLine;
			  CardType[] types = CardType.values();
			  //Read File Line By Line
			  while ((strLine = br.readLine()) != null)   {
			  // Print the content on the console
				  String [] line = strLine.split(" ");
				  if (line[1].equals(line[2]))
				  {
					  // handle pair
					  CardNumber number = getCardNumber(line[1]);
					  Double odds = Double.valueOf(line[0])/100.0;
					  //System.out.println(odds);
					  for (int i = 0; i < types.length; i++)
					  {
						  Card c1 = new Card(number,types[i]);
						  for (int j = i+1; j < types.length; j++)
						  {
							  Card c2 = new Card(number,types[j]);
							  Hand h = new Hand(c1, c2, odds);
							  _oddsBeforeFlop.put(h, odds);
						  }
					  }
					  
				  }
				  else if (line[3].equals("unsuited"))
				  {
					  CardNumber n1 = getCardNumber(line[1]);
					  CardNumber n2 = getCardNumber(line[2]);
					  Double odds = Double.valueOf(line[0]);
					  //System.out.println(odds);
					  for (int i = 0; i < types.length; i++)
					  {
						  Card c1 = new Card(n1,types[i]);
						  for (int j = 0; j < types.length; j++)
						  {
							  if (i != j)
							  {
								  Card c2 = new Card(n2,types[j]);
								  Hand h = new Hand(c1, c2, odds);
								  _oddsBeforeFlop.put(h, odds);
							  }
							  
						  }
					  }
				  }
				  else
				  {
					  CardNumber n1 = getCardNumber(line[1]);
					  CardNumber n2 = getCardNumber(line[2]);
					  Double odds = Double.valueOf(line[0]);
					  //System.out.println(odds);
					  for (int i = 0; i < types.length; i++)
					  {
						    Card c1 = new Card(n1,types[i]);
							Card c2 = new Card(n2,types[i]);
							Hand h = new Hand(c1, c2, odds);
							_oddsBeforeFlop.put(h, odds);
					  }
				  }
			  }
			  //Close the input stream
			  in.close();
			    }catch (Exception e){//Catch exception if any
			  System.err.println("Error: " + e.getMessage());
			  }
		
	}
	public static CardNumber getCardNumber(String card) {
		
		try
		{
			return CardNumber.values()[Integer.valueOf(card)-1];
		}
		catch (NumberFormatException e)
		{
			if (card.equals("A"))
				return CardNumber.values()[0];
			if (card.equals("T"))
				return CardNumber.values()[9];
			if (card.equals("J"))
				return CardNumber.values()[10];
			if (card.equals("Q"))
				return CardNumber.values()[11];
			if (card.equals("K"))
				return CardNumber.values()[12];
		}
		return null;
	}
	private void initHandValues(InputStream fstream) {
		_handValues = new HashMap<Double, Integer>();
		_flushHandValues = new HashMap<Double, Integer>();
		try{
			  // Open the file that is the first 
			  // command line parameter

			  // Get the object of DataInputStream
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));
			  String strLine;
			  //Read File Line By Line
			  while ((strLine = br.readLine()) != null)   {
			  // Print the content on the console
				  String [] line = strLine.split("\t");
				  String [] hand = line[1].split(" ");
				  double handID = calcHandId(hand);
				  if (line[2].contains("Flush"))
					  _flushHandValues.put(new Double(handID), Integer.valueOf(line[0]));
				  else
					  _handValues.put(new Double(handID), Integer.valueOf(line[0]));				  
			  }
			  //Close the input stream
			  in.close();
			    }catch (Exception e){//Catch exception if any
			    	e.printStackTrace();
			    	System.err.println("Error: " + e.getMessage());
			  }
	}
	protected static double calcHandId(Card[] hand) {
		
		double id = 1;
		for (int i = 0; i < hand.length; i++)
			id *= _numValues.get(hand[i].getNumber());
		return id;
	}
	private static double calcHandId(String[] hand) {	
		
		double id = 1;
		for (int i = 0; i < hand.length; i++)
			id *= _numValues.get(Odds.getCardNumber(hand[i]));
		return id;
	}
	private void initNumValues() {
		_numValues = new HashMap<CardNumber,Double>();
		_numValues.put(CardNumber.Two, new Double(2));
		_numValues.put(CardNumber.Three, new Double(3));
		_numValues.put(CardNumber.Four, new Double(5));
		_numValues.put(CardNumber.Five, new Double(7));
		_numValues.put(CardNumber.Six, new Double(11));
		_numValues.put(CardNumber.Seven, new Double(13));
		_numValues.put(CardNumber.Eight, new Double(17));
		_numValues.put(CardNumber.Nine, new Double(19));
		_numValues.put(CardNumber.Ten, new Double(23));
		_numValues.put(CardNumber.Jack, new Double(29));
		_numValues.put(CardNumber.Queen, new Double(31));
		_numValues.put(CardNumber.King, new Double(37));
		_numValues.put(CardNumber.Ace, new Double(41));
	}

	protected static int getHandValue(Hand h)
	{
		Card[] table = h.getTable();
		// without the hand
		int max = _handValues.get(new Double(calcHandId(table)));
		Card first = h.getFirst();
		Card second = h.getSecond();
		// with only 1 of the 2 cards
		for (int i = 0; i < table.length; i++)
		{
			Card tmp = table[i];
			table[i] = first;
			int val = _handValues.get(new Double(calcHandId(table)));
			if (val > max)
				max = val;
			table[i] = second;
			val = _handValues.get(new Double(calcHandId(table)));
			if (val > max)
				max = val;
			table[i] = tmp;
		}
		// with both 2 cards
		for (int i = 0; i < table.length; i++)
		{
			Card t1 = table[i];
			table[i] = first;
			for (int j = i+1; j < table.length; j++)
			{
				Card t2 = table[j];
				table[j] = second;
				double d = calcHandId(table); 
				int val = _handValues.get(new Double(d));
				if (val > max)
					max = val;
				table[j] = t2;
			}
			table[i] = t1;
		}
		return max;
	}

	public boolean strongHand(Hand h1, Hand h2) {
		return getHandValue(h1) < getHandValue(h2);
	}

/*
	public  double calcOddsAfterRiver (Hand hand)
	{
		Deck d = Deck.getInstance();
		Vector<Card> deck = d.getCopyOfDeck();
		Card first = hand.getFirst(); 
		deck.remove(first);
		Card second = hand.getSecond();
		deck.remove(second);
		Card [] table = hand.getTable();
		for (int i = 0; i < table.length; i++)
		{
			if (table[i] == null)
				break;
			deck.remove(table[i]);
		}
		double win = 0, total = 0;
		for (int i = 0; i < deck.size(); i++)
		{
			Card c1 = deck.get(i);
			for (int j = i+1; j < deck.size(); j++, total++)
			{
				Card c2 = deck.get(j);
				if (strongHand(hand, new Hand(c1,c2, table)))
					win++;
			}
		}
		return win/total;
		
	}
	private void createFullHandOddsFile()
	{
		System.out.println("Start write to file");
		try{
			  // Create file 
			  FileWriter fstream = new FileWriter("fullTableOdds.txt");
			  BufferedWriter out = new BufferedWriter(fstream);
			  
			  //Close the output stream
		
			Deck d = Deck.getInstance();
			Vector<Card> deck = d.getCopyOfDeck();
			// choose 2 cards.
			
			for (int i = 0; i < deck.size(); i++)
			{
				Card first = deck.remove(i);
				for (int j = i; j < deck.size(); j++)
				{
					Card second = deck.remove(j);
					addAllTableToFile(first,second, deck, out);
					deck.add(j, second);
				}
				deck.add(i,first);
			}
			
			System.out.println("finish write to file");
			out.close();
		}
		catch (Exception e){//Catch exception if any
			e.printStackTrace();  
			System.err.println("Error: " + e.getMessage());
		}
	}
	
	private void addAllTableToFile(Card first, Card second, Vector<Card> deck, BufferedWriter out) throws IOException{
		Card[] table = new Card[5];
		int count = 0;
		for (int i1 = 0; i1 < deck.size(); i1++)
		{
			table[0]= deck.remove(i1);
			for (int i2 = i1; i2 < deck.size(); i2++)
			{
				table[1]= deck.remove(i2);
				for (int i3 = i2; i3 < deck.size(); i3++)
				{
					table[2]= deck.remove(i3);
					for (int i4 = i3; i4 < deck.size(); i4++)
					{
						table[3]= deck.remove(i4);
						for (int i5 = i4; i5 < deck.size(); i5++, count++)
						{
							table[4]= deck.remove(i5);
							Hand h = new Hand(first, second, table);
							double odds = getOddsToWinAfterRiver(h,deck);
							writeOdds(first,second,table,odds, out);
							_fullHandOdds.put(h, new Double(odds));
							deck.add(i5,table[4]);
						}	
						deck.add(i4, table[3]);
					}	
					deck.add(i3, table[2]);
				}	
				deck.add(i2, table[1]);
			}
			deck.add(i1, table[0]);
		}
		System.out.println(count);
		
	}
	private static void writeOdds(Card first, Card second, Card[] table, double odds, BufferedWriter out) throws IOException {
		String line = first.toString() + second.toString();
		for (int i = 0; i < table.length; i++)
			line += table[i].toString();
		line += odds;
		out.write(line + "\n");
		
	}
	private double getOddsToWinAfterFlop(Hand h1, Vector<Card> deck) {
		double win = 0, total = 0;
		Card[] table = h1.getTable();
		for (int k = 0; k < deck.size(); k++)
		{
			table[3] = deck.remove(k);
			for (int k2 = k; k2 < deck.size(); k2++)
			{
				table[4] = deck.remove(k2);
				// check all the possibols 
				for (int i = 0; i < deck.size(); i++)
				{
					Card first = deck.get(i);
					for (int j = i+1; j < deck.size(); j++)
					{
						Card second = deck.get(j);
						Hand h2 = new Hand(first,second,h1.getTable());
						if (strongHand(h1, h2))
							win++;
						total++;
					}
				}
			}
			deck.add(k,table[3]);
		}
		return win/total;
	}
	private double getOddsToWinAfterTurn(Hand h1, Vector<Card> deck) {
		double win = 0, total = 0;
		for (int i = 0; i < deck.size(); i++)
		{
			Card first = deck.get(i);
			for (int j = i+1; j < deck.size(); j++)
			{
				Card second = deck.get(j);
				Hand h2 = new Hand(first,second,h1.getTable());
				if (strongHand(h1, h2))
					win++;
				total++;
			}
		}
		return win/total;
	}
	private double getOddsToWinAfterRiver(Hand h1, Vector<Card> deck) {
		double win = 0, total = 0;
		for (int i = 0; i < deck.size(); i++)
		{
			Card first = deck.get(i);
			for (int j = i+1; j < deck.size(); j++)
			{
				Card second = deck.get(j);
				Hand h2 = new Hand(first,second,h1.getTable());
				if (strongHand(h1, h2))
					win++;
				total++;
			}
		}
		return win/total;
	}
	
	private void test() {
		// hand 1
		Card c1 = new Card(CardNumber.Ace, CardType.Club);
		Card c2 = new Card(CardNumber.Ace, CardType.Diamond);
		// hand 2
		Card c3 = new Card(CardNumber.Four, CardType.Club);
		Card c4 = new Card(CardNumber.Eight, CardType.Diamond);
		// table
		Card[] table = new Card[5];
		table[0] = new Card(CardNumber.Ace, CardType.Spade);
		table[1] = new Card(CardNumber.Ace, CardType.Heart);
		table[2] = new Card(CardNumber.King, CardType.Diamond);
		table[3] = new Card(CardNumber.Seven, CardType.Diamond);
		table[4] = new Card(CardNumber.Ten, CardType.Spade);
		
		Hand h1 = new Hand(c1, c2, table);
		Hand h2 = new Hand(c3, c4, table);
		if (strongHand(h1, h2))
			System.out.println("Stronger");
		
	}
*/
	public double getInitOdds(Hand hand) {
		Set<Hand> hands = _oddsBeforeFlop.keySet();
		for (Hand h: hands)
			if (equalHands(hand,h) )
				return _oddsBeforeFlop.get(h);
		return 0;
	}

	private boolean equalHands(Hand h1, Hand h2) {
		Card f1,f2,s1,s2;
		f1 = h1.getFirst();
		f2 = h2.getFirst();
		s1 = h1.getSecond();
		s2 = h2.getSecond();
		return  ((f1.equals(f2)&& s1.equals(s2)) || (f1.equals(s2) && s1.equals(f2)) );
	}
}
